use rustcloud_core::{CircuitBreaker, LoadBalancer, Request, Response, ServiceError, ServiceRegistry, ServiceResult, Transport, TransportConfig};
use std::sync::Arc;
use tokio::time::{sleep, Duration};

/// Dynamic Service Proxy
#[derive(Clone)]
pub struct ServiceProxy<CB: CircuitBreaker> {
    registry: Arc<dyn ServiceRegistry>,
    load_balancer: Arc<dyn LoadBalancer>,
    transport: Arc<dyn Transport>,
    circuit_breaker: Arc<CB>,
}

impl<CB: CircuitBreaker> ServiceProxy<CB> {
    pub fn new(
        registry: Arc<dyn ServiceRegistry>,
        load_balancer: Arc<dyn LoadBalancer>,
        transport: Arc<dyn Transport>,
        circuit_breaker: Arc<CB>,
    ) -> Self {
        Self {
            registry,
            load_balancer,
            transport,
            circuit_breaker,
        }
    }

    pub async fn call(&self, request: Request, config: &TransportConfig) -> ServiceResult<Response> {
        let mut last_error: Option<ServiceError> = None;

        for attempt in 0..=config.retries {
            if attempt > 0 {
                sleep(Duration::from_millis(config.retry_delay)).await;
            }

            let service_name_cloned = request.service_name.clone();
            let request_cloned = request.clone();

            // Clone Arcs for the closure to own
            let registry_cloned = self.registry.clone();
            let load_balancer_cloned = self.load_balancer.clone();
            let transport_cloned = self.transport.clone();

            let result = self.circuit_breaker.call(
                &service_name_cloned.clone(), // Fix E0505
                Box::new(move || {
                    // Use the cloned Arcs directly
                    let registry = registry_cloned;
                    let load_balancer = load_balancer_cloned;
                    let transport = transport_cloned;
                    Box::pin(async move {
                        let instances = registry.discover(&service_name_cloned).await?;
                        if instances.is_empty() {
                            return Err(ServiceError::NoAvailableInstances(service_name_cloned));
                        }

                        let instance = load_balancer.select(&service_name_cloned, &instances).await?
                            .ok_or_else(|| ServiceError::NoAvailableInstances(service_name_cloned))?;

                        transport.send(request_cloned, &instance).await
                    })
                }),
            ).await;

            match result {
                Ok(response) => {
                    if response.is_success() || (response.status >= 400 && response.status < 500) {
                        return Ok(response);
                    }
                    last_error = Some(ServiceError::NetworkError(format!("Server error: {}", response.status)));
                }
                Err(e) => {
                    if e.is_network_error() || e.is_circuit_breaker_open() {
                        last_error = Some(e);
                    } else {
                        return Err(e);
                    }
                }
            }
        }

        Err(last_error.unwrap_or_else(|| ServiceError::NetworkError("All retry attempts failed".to_string())))
    }
}